
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <libxml/encoding.h>
#include <libxml/xmlwriter.h>
#include <libxml/parser.h>
#include <pwd.h>
#include <sys/types.h>
/*
<dev class="software">
  <dev class="device" id="1" describe="left_wheel">
    <dev class="interface" id="1" type="dc">
        <dev class="point" id="1" type="point" pin="3" pwmchip="2" pwm="3"></dev>
        <dev class="point" id="2" type="point" pin="3" pwmchip="2" pwm="3"></dev>
    </dev>
  </dev>
  <dev class="device" id="2" describe="right_wheel">
    <dev class="interface" id="2" type="dc">
        <dev class="point" id="3" type="point" pin="3" pwmchip="2" pwm="3"></dev>
        <dev class="point" id="4" type="point" pin="3" pwmchip="2" pwm="3"></dev>
    </dev>
  </dev>
</dev>
 */
#include <stdio.h>
/*
char *get_base_name(const char *name)
{
  gchar **tokens = g_strsplit(name, "_", -1);
  gchar *res = NULL;

  if (tokens[0] != NULL)
    res = strdup(tokens[0]);
  g_strfreev(tokens);
  return res;
}
void *get_function(const char *name)
{
  //g_debug("%s L%d f-%s %s ", __FILE__, __LINE__, __func__, name);
  if (!strcmp(name, "software_software_new")) {
    return software_software_new;
  } else if (!strcmp(name, "device_device_new")) {
    return device_device_new;
  } else if (!strcmp(name, "interface_interface_new")) {
    return interface_interface_new;
  } else if (!strcmp(name, "point_point_new")) {
    return point_point_new;
  } else if (!strcmp(name, "describe_value_new")) {
    return describe_value_new;
  } else {
    g_error("invalid object:%s", name);
  }
  return NULL;
}
int node_add_value(xmlNodePtr node, const char *name, GValue *value)
{

  char buf[8];

  if (G_VALUE_HOLDS_STRING(value)) {
    if (g_value_get_string(value))
    xmlNewProp(node, BAD_CAST name,
        BAD_CAST g_value_get_string(value));
  } else if (G_VALUE_HOLDS_INT(value)) {
    g_snprintf(buf, sizeof(buf), "%"G_GINT32_FORMAT, g_value_get_int(value));
    xmlNewProp(node, BAD_CAST name, BAD_CAST buf);
  } else if (G_VALUE_HOLDS_UINT(value)) {
    g_snprintf(buf, sizeof(buf), "%u", g_value_get_uint(value));
    xmlNewProp(node, BAD_CAST name, BAD_CAST buf);
  } else if (G_VALUE_HOLDS_LONG(value)) {
    g_snprintf(buf, sizeof(buf), "%ld", g_value_get_long(value));
    xmlNewProp(node, BAD_CAST name, BAD_CAST buf);
  } else if (G_VALUE_HOLDS_ULONG(value)) {
    g_snprintf(buf, sizeof(buf), "%lu", g_value_get_ulong(value));
    xmlNewProp(node, BAD_CAST name, BAD_CAST buf);
  } else if (G_VALUE_HOLDS_INT64(value)) {
    g_snprintf(buf, sizeof(buf), "%"G_GINT64_FORMAT, g_value_get_int64(value));
    xmlNewProp(node, BAD_CAST name, BAD_CAST buf);
  } else if (G_VALUE_HOLDS_FLOAT(value)) {
    g_snprintf(buf, sizeof(buf), "%f", g_value_get_float(value));
    xmlNewProp(node, BAD_CAST name, BAD_CAST buf);
  } else if (G_VALUE_HOLDS_DOUBLE(value)) {
    g_snprintf(buf, sizeof(buf), "%f", g_value_get_double(value));
    xmlNewProp(node, BAD_CAST name, BAD_CAST buf);
  } else if (G_VALUE_HOLDS_BOOLEAN(value)) {
    g_snprintf(buf, sizeof(buf), "%d", g_value_get_boolean(value));
    xmlNewProp(node, BAD_CAST name, BAD_CAST buf);
  } else if (G_VALUE_HOLDS_POINTER(value)) {
    //printf("%p", g_value_get_pointer(value));
  } else {
    printf("Unknown type");
  }

  return true;
}
void *xml_driver__object_to_node(GObject *obj)
{
  if (!obj) {
    printf("[WARNING] F-%s L%d f-%s obj is NULL",
        __FILE__, __LINE__, __func__);
    return NULL;
  }
  GObjectClass *obj_class = G_OBJECT_GET_CLASS(obj);
  GParamSpec **properties;
  guint n_properties, i;
  xmlNodePtr node = NULL;

  node = xmlNewNode(NULL, BAD_CAST "dev");
  properties = g_object_class_list_properties(
      obj_class, &n_properties);
  for (i = 0; i < n_properties; i++) {
      GParamSpec *pspec = properties[i];
      GValue value = G_VALUE_INIT;

      g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
      g_object_get_property(obj, g_param_spec_get_name(pspec), &value);
      node_add_value(node, g_param_spec_get_name(pspec), &value);

      g_value_unset(&value);
  }

  GObject *son, *son_next;
  xmlNodePtr child_node = NULL;
  g_object_get(obj, "son", &son, NULL);
  while (son) {
    g_object_get(son, "next", &son_next, NULL);
    child_node = xml_driver__object_to_node(son);
    xmlAddChild(node, child_node);
    son = son_next;
  }
  g_free(properties);
  return node;
}
void *xml_driver__objects_to_node(GObject *obj)
{
  GObject *next;
  xmlNodePtr node = NULL;
  xmlNodePtr child_node = NULL;

  node = xmlNewNode(NULL, BAD_CAST "dev");
  while (obj) {
    g_object_get(obj, "next", &next, NULL);
    child_node = xml_driver__object_to_node(obj);
    xmlAddChild(node, child_node);
    obj = next;
  }
  return node;
}
typedef void *(*func_type_new)();
void *xml_driver__node_to_object(void *node, void *obj)
{
  xmlNodePtr now = node;
  GObject *object = obj;
  xmlChar *xml_char = NULL;
  xmlAttr *attr = NULL;
  GObjectClass *obj_class = NULL;
  GParamSpec *pspec = NULL;
  int objectNull = false;

  if (!object) {
    objectNull = true;
    const char *class;
    xml_char = xmlGetProp(now, BAD_CAST "class");
    if (!xml_char) {
      printf("[WARNING] %s L%d f-%s no class\n", __FILE__, __LINE__, __func__);
      return NULL;
    }
    class = (char *)xml_char;
    char func_name[1024];
    snprintf(func_name, sizeof(func_name), "%s_new", class);
    func_type_new func = get_function(func_name);
    if (func)
      object = func();
    else
      printf("[WARNING] %s L%d f-%s no %s func", __FILE__, __LINE__, __func__,
          func_name);
  }
  if (!object) {
    return object;
  }
  //printf("[WARNING] object:%p", object);
  char *chars;
  g_object_get(object, "class", &chars, NULL);
  if (chars) {
    //g_debug("f-%s class:%s", __func__, chars);
    free(chars);
  }
  obj_class = G_OBJECT_GET_CLASS(object);
  const char *pro_name, *pro_value;
  for (attr = now->properties; attr; attr = attr->next) {
    xmlChar *value = xmlNodeGetContent(attr->children);
    //printf("L%d f-%s Property: %s, Value: %s\n", __LINE__, __func__,
      //  attr->name, value);
    pro_name = (char *)(attr->name);
    pro_value = (char *)value;
    pspec = g_object_class_find_property(obj_class, pro_name);
    if (pspec) {
      if (G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_STRING) {
        g_object_set(object, pro_name, pro_value, NULL);
      } else if (G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_INT) {
        //printf("[WARNING] set int");
        g_object_set(object, pro_name, atoi(pro_value), NULL);
      } else if (G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_BOOLEAN) {
        g_object_set(object, pro_name, atoi(pro_value), NULL);
      } else if (G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_INT64) {
        gint64 int64 = 0;
        if (!strcmp(pro_name, "id")) {
          g_object_get(object, pro_name, &int64, NULL);
          if (!int64) {
            int64 = atol(pro_value);
            g_object_set(object, pro_name, int64, NULL);
          }
        } else {
          int64 = atol(pro_value);
          g_object_set(object, pro_name, int64, NULL);
        }
      } else if (G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_FLOAT) {
        g_object_set(object, pro_name, atof(pro_value), NULL);
      }
    }
    xmlFree(value);
  }
  GObject *obj_child = NULL, *child_start = NULL, *child_next = NULL;
  xmlNode *child;
  if (object) {
    g_object_get(object, "son", &obj_child, NULL);
    if (obj_child) {
      child_start = obj_child;
    }
    //printf("[WARNING] child:%p", child_start);
  }
  for (child = now->children; child; child = child->next) {
    if (child->type == XML_ELEMENT_NODE) {
      if (!objectNull)
        g_object_get(obj_child, "next", &child_next, NULL);
      else
        obj_child = NULL;
      //printf("[WARNING] object %p obj_child:%p", object, obj_child);
      obj_child = xml_driver__node_to_object(child, obj_child);
      if (objectNull) {
        if (!child_start) {
          //printf("[WARNING] %d", __LINE__);
          child_start = obj_child;
      //printf("[WARNING] child start:%p", child_start);
        } else {
      //printf("[WARNING] child start:%p %p", child_start, obj_child);
          object_add_end(child_start, obj_child);
        }
      }
      g_object_set(obj_child, "parent", object, NULL);
      if (!objectNull)
        obj_child = child_next;
    }
  }
  GObject *son;
  g_object_get(object, "son", &son, NULL);
  if (child_start && (!son))
    g_object_set(object, "son", child_start, NULL);
  return object;
}
void *xml_driver__node_to_objects(void *node)
{
  xmlNode *now = node, *child;
  GObject *obj_child = NULL, *child_start = NULL;

  for (child = now->children; child; child = child->next) {
    if (child->type == XML_ELEMENT_NODE) {
      obj_child = xml_driver__node_to_object(child, NULL);
      if (!child_start)
        child_start = obj_child;
      else
        object_add_end(child_start, obj_child);
    }
  }
  return child_start;
}
void *xml_driver__content_to_object(const char *content, void *object)
{
  xmlDocPtr doc;
  xmlNodePtr root;
  GObject *obj = NULL;
  doc = xmlParseDoc(BAD_CAST content);
  root = xmlDocGetRootElement(doc);
  xmlChar *xml_char = NULL;
  xml_char = xmlGetProp(root, BAD_CAST "class");
  if (!xml_char) {
    obj =  xml_driver__node_to_objects(root);
  } else {
    xmlFree(xml_char);
    obj = xml_driver__node_to_object(root, object);
  }
  xmlFreeDoc(doc);
  return obj;
}
void *xml_driver__file_to_object(const char *filename, void *object)
{
  char *content = NULL;
  size_t size;
  GObject *obj = NULL;

  if (g_file_get_contents(filename, &content, &size, NULL)) {
    printf("[DEBUG]xml_driver__file:%s\n", filename);
    obj = xml_driver__content_to_object(content, object);
  } else {
    printf("[----ERROR] no %s\n", filename);
    exit(1);
  }
  return obj;
}
char *xml_driver__get_config_filename(const char *name)
{
  char path[1024];

  if (name) {
    snprintf(path, sizeof(path), "/etc/%s/%s.xml", name, name);
  } else {
    snprintf(path, sizeof(path), "/etc/%s/%s.xml",
        g_get_prgname(), g_get_prgname());
  }

  return strdup(path);
}
void *xml_driver__software_init(int argc, char **argv)
{
  const char *xml_driver__file = NULL;

  if ((argc == 2) && (!strcmp(argv[1], "-h"))) {
    printf("Usage: %s [OPTION]... [FILE]...\n\
%s\n\
-c\tconfig file\n\
Examples:\n\
\t%s -c config.xml\n", g_get_prgname(), g_get_prgname(), g_get_prgname());
    exit(1);
  } else if ((argc == 3) && (!strcmp(argv[1], "-c"))) {
    xml_driver__file = argv[2];
  } else {
    xml_driver__file = xml_driver__get_config_filename(NULL);
  }
  void *software = xml_driver__file_to_object(xml_driver__file, NULL);
//  g_object_set(software, "xml_driver__file", xml_driver__file, NULL);
  return software;
}
char *xml_driver__get_xml_driver__file(const char *name)
{
  char file[1024];

  if (name) {
    snprintf(file, sizeof(file), "/etc/%s/%s.xml", name, name);
  } else {
    snprintf(file, sizeof(file), "/etc/%s/%s.xml", g_get_prgname(),
        g_get_prgname());
  }
  return strdup(file);
}
void xml_driver__software_to_xmlfile(void *sft)
{
  char file[1024], *res = NULL, *name = NULL;
  GObject *software = sft;
  xmlNodePtr node = NULL;
  uid_t uid = getuid();
  struct passwd *pw = getpwuid(uid);

  if (!sft) {
    printf("[WARNING] F-%s L%d f-%s no obj\n", __FILE__, __LINE__, __func__);
    return;
  }
  node = xml_driver__object_to_node(G_OBJECT(software));
  res = xml_driver__node_to_str(node);
  if (!res) {
    printf("[WARNING] F-%s L%d f-%s no res\n", __FILE__, __LINE__, __func__);
    return;
  }
  g_object_get(software, "name", &name, NULL);
  snprintf(file, sizeof(file), "%s/.config/ts/%s.xml",
      pw->pw_dir, name);
  g_file_set_contents(file, res, strlen(res), NULL);
  if (name)
    free(name);
}
void xml_driver__points_of_interfaces_set_update(void *p, bool status)
{
  GObject *ifa = NULL, *ifa_next;
  GObject *pit = NULL, *pit_next;

  ifa = p;
  while (ifa) {
    g_object_get(ifa, "next", &ifa_next, "son", &pit, NULL);
//        g_object_set(ifa, "status", FALSE, NULL);
    while (pit) {
      g_object_get(pit, "next", &pit_next, NULL);
      g_object_set(pit, "update", status, NULL);
      pit = pit_next;
    }
    ifa = ifa_next;
  }
}
*/
/*
 * source:
  <dev test="1" class="software_software" id="4111" test="1" name="tcsiec101" type="5">
    <dev id="13" class="device_device" enable="1" name="task" deviceId="0">
      <dev id="23" class="interface_interface" dontMove="1" isapiIp="192.168.1.66" isapiUser="admin" isapiPwd="ibse123456" pointInfo="1" interval="dog" enable="1" content="AR001" port="20" priority="4">
        <dev id="31" class="point_point" type="13"/>
        <dev id="32" class="point_point" type="14"/>
        <dev id="33" class="point_point" type="2"/>
        <dev id="34" class="point_point" type="3"/>
      </dev>
    </dev>
  </dev>
 * result:
<root id="4120" name="serial">
    <child id="1" status="1">
      <child id="17" status="1">
        <child id="14" value="53.81" status="1" time="2025-10-17 10:21:57" timestamp="1760667717"/>
        <child id="19" value="0.91" status="1" time="2025-10-17 10:21:57" timestamp="1760667717"/>
        <child id="21" value="21.00" status="1" time="2025-10-17 10:21:57" timestamp="1760667717"/>
        <child id="23" value="4266.00" status="1" time="2025-10-17 10:21:57" timestamp="1760667717"/>
        <child id="25" value="56906.00" status="1" time="2025-10-17 10:21:57" timestamp="1760667717"/>
      </child>
    </child>
  </root>
  */
char *xml_driver__copy_node_to_str(void *node)
{
  int rc;
  char *res = NULL;
  xmlNodePtr root = xmlCopyNode(node, 2);
  xmlBufferPtr xmlbuf = NULL;

  if (!node) {
    printf("[WARNING] no node\n");
    return NULL;
  }
  xmlbuf = xmlBufferCreate();
  rc = xmlNodeDump(xmlbuf, NULL, root, 1, 1);
  if (rc == -1) {
    printf("[WARNING] xmlnodedump error\n");
  } else {
    res = strdup((char *)xmlbuf->content);
  }
  if (xmlbuf)
    xmlBufferFree(xmlbuf);
  xmlFreeNode(root);
  return res;
}
char *xml_driver__node_to_str_no_free(void *node)
{
  int rc;
  char *res = NULL;
  xmlNodePtr root = node;
  xmlBufferPtr xmlbuf = NULL;

  if (!node) {
    printf("[WARNING] no node\n");
    return NULL;
  }
  xmlbuf = xmlBufferCreate();
  rc = xmlNodeDump(xmlbuf, NULL, root, 1, 1);
  if (rc == -1) {
    printf("[WARNING] xmlnodedump error\n");
  } else {
    res = strdup((char *)xmlbuf->content);
  }
  if (xmlbuf)
    xmlBufferFree(xmlbuf);
  return res;
}
char *xml_driver__node_to_str(void *node)
{
  int rc;
  char *res = NULL;
  xmlNodePtr root = node;
  xmlBufferPtr xmlbuf = NULL;

  if (!node) {
    printf("[WARNING] no node\n");
    return NULL;
  }
  xmlbuf = xmlBufferCreate();
  rc = xmlNodeDump(xmlbuf, NULL, root, 1, 1);
  if (rc == -1) {
    printf("[WARNING] xmlnodedump error\n");
  } else {
    res = strdup((char *)xmlbuf->content);
  }
  if (xmlbuf)
    xmlBufferFree(xmlbuf);
  xmlFreeNode(root);
  return res;
}
void *xml_driver__filtrate_by_update(void *node)
{
  xmlNodePtr rootNode = node, childNode, grandChildNode,
             greatGrandChildNode;
  xmlNodePtr root = NULL, child = NULL, grandChild = NULL,
             greatGrandChild = NULL;
  xmlChar *xml_char = NULL, *tmp_char = NULL;

  childNode = xmlFirstElementChild(rootNode);//device
  while (childNode) {
    xml_char = xmlGetProp(childNode, BAD_CAST "class");
    grandChildNode = xmlFirstElementChild(childNode);
    while (grandChildNode) {//interface
      greatGrandChildNode = xmlFirstElementChild(grandChildNode);
      while (greatGrandChildNode) {//point
        xml_char = xmlGetProp(greatGrandChildNode, BAD_CAST "update");
        if (!xml_char) {
          greatGrandChildNode = xmlNextElementSibling(
              greatGrandChildNode);
          continue;
        }
        if (strcmp((char *)xml_char, "1") == 0) {
          greatGrandChild = xmlNewNode(NULL, BAD_CAST "child");
          if (!grandChild) {
            grandChild = xmlNewNode(NULL, BAD_CAST "child");
            tmp_char = xmlGetProp(grandChildNode, BAD_CAST "id");
            if (tmp_char) {
              xmlNewProp(grandChild, BAD_CAST "id", tmp_char);
              xmlFree(tmp_char);
            }
            if (!child) {
              child = xmlNewNode(NULL, BAD_CAST "child");
              tmp_char = xmlGetProp(childNode, BAD_CAST "id");
              if (tmp_char) {
                xmlNewProp(child, BAD_CAST "id", tmp_char);
                xmlFree(tmp_char);
              }
              if (!root) {
                root = xmlNewNode(NULL, BAD_CAST "root");
                tmp_char = xmlGetProp(rootNode, BAD_CAST "id");
                if (tmp_char) {
                  xmlNewProp(root, BAD_CAST "id", tmp_char);
                  xmlFree(tmp_char);
                }
              }
              xmlAddChild(root, child);
            }
            xmlAddChild(child, grandChild);
          }
          xmlAddChild(grandChild, greatGrandChild);
          xmlFree(xml_char);
          xml_char = xmlGetProp(greatGrandChildNode, BAD_CAST "id");
          if (xml_char) {
            xmlNewProp(greatGrandChild, BAD_CAST "id", xml_char);
            xmlFree(xml_char);
          }
          xml_char = xmlGetProp(greatGrandChildNode, BAD_CAST "file1");
          if (xml_char) {
            xmlNewProp(greatGrandChild, BAD_CAST "file1", xml_char);
            xmlFree(xml_char);
          }
          xml_char = xmlGetProp(greatGrandChildNode, BAD_CAST "value");
          if (xml_char) {
            xmlNewProp(greatGrandChild, BAD_CAST "value", xml_char);
            xmlFree(xml_char);
          }
          xml_char = xmlGetProp(greatGrandChildNode, BAD_CAST "time");
          if (xml_char) {
            xmlNewProp(greatGrandChild, BAD_CAST "time", xml_char);
            xmlFree(xml_char);
          }
          xml_char = xmlGetProp(greatGrandChildNode, BAD_CAST "timestamp");
          if (xml_char) {
            xmlNewProp(greatGrandChild, BAD_CAST "timestamp", xml_char);
            xmlFree(xml_char);
          }
        }
        greatGrandChild = NULL;
        greatGrandChildNode = xmlNextElementSibling(
            greatGrandChildNode);
      }
      grandChild = NULL;
      grandChildNode = xmlNextElementSibling(grandChildNode);
    }
    child = NULL;
    childNode = xmlNextElementSibling(childNode);
  }
  return root;
}

xmlNodePtr xmldriver__getRootNode(xmlNodePtr node)
{
  while (node->parent != NULL) {
    node = node->parent;
  }
  return node;
}
